home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / contrib / ServiceMail / src / mesh / parsemime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-18  |  13.6 KB  |  433 lines

  1. /*
  2.  * parsemime.c -- functions to parse incoming mail messages in
  3.  * MIME format
  4.  *
  5.  * 3-17-93   weber@eitech.com really fixed trailing newline bug in from7bit
  6.  * 2-25-93   weber@eitech.com fixed trailing newline bug (thanks Bob Sum)
  7.  * 2-18-93   weber@eitech.com added extraction of x-splitsize: field
  8.  * 2-18-93   weber@eitech.com added extraction of apparently-to: field
  9.  * 1-21-93   weber@eitech.com adding extraction of To: field
  10.  * 9-8-92    ekr@eitech.com   v 1.1
  11.  * 27-Jun-92 weber@eitech.com marked ServiceMail(tm) v1.0
  12.  * 25-May-92 weber@eitech.com created
  13.  *
  14.  * Copyright (c)  1992 Enterprise Integration Technologies Corporation
  15.  *
  16.  * Permission to use, copy, modify, distribute, and sell this software and 
  17.  * its documentation for any purpose is hereby granted without fee, provided
  18.  * that (i) the above copyright notices and this permission notice appear in
  19.  * all copies of the software and related documentation, and (ii) the name of
  20.  * Enterprise Integration Technologies Corporation may not be used in any 
  21.  * advertising or publicity relating to the software without the specific, 
  22.  * prior written permission of Enterprise Integration Technologies Corporation.
  23.  * 
  24.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  25.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  26.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  27.   *
  28.  * IN NO EVENT SHALL ENTERPRISE INTEGRATION TECHNOLOGIES CORPORATION  BE
  29.  * LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
  30.  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  31.  * PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
  32.  * THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  33.  * PERFORMANCE OF THIS SOFTWARE.
  34.  */
  35.  
  36. #include <pwd.h>
  37. #include <sys/types.h>
  38. #include <sys/dir.h>
  39.  
  40. #include "mesh.h"
  41.  
  42. #ifndef TEMPDIR
  43. #define TEMPDIR /tmp
  44. #endif
  45. char scratchdir[MAXFILENAMELEN];
  46.  
  47. char *Trim(s)
  48.   char *s;
  49. {
  50.   char *t;
  51.   
  52.   t = s + strlen(s);
  53.   while (t-- > s && isspace(*t)) *t = NULL;
  54.   return s;
  55. }
  56.  
  57. HandleMessage(instream)
  58.      FILE *instream;
  59. {
  60.   struct MessageInfo minfo;
  61.   char  killdircmd[MAXFILENAMELEN+16];
  62.   char origdir[MAXFILENAMELEN];  
  63.   /*getcwd() is lame. Use it if you must(like you don't have getwd())*/ 
  64.   getwd(origdir);
  65.   sprintf(scratchdir, "%s/mesh.%d", TEMPDIR, getpid());
  66.   mkdir(scratchdir, 0700);
  67.   chdir(scratchdir);
  68.   
  69.   InitHeader(&minfo);
  70.   ParseHeader(&minfo, instream);
  71.   ParseBody(&minfo, instream, NULL, NULL);
  72.   if (strcasecmp(minfo.content.type, "message") ||
  73.       strcasecmp(minfo.content.format, "partial")) StartJob(&minfo);
  74.   
  75.   sprintf(killdircmd, "rm -r %s", scratchdir);
  76.   system(killdircmd);
  77.   FreeGarbage(&minfo);
  78.   chdir(origdir);
  79. }
  80.  
  81. InitHeader(message)
  82.      struct MessageInfo *message;
  83. {
  84.   message->from = message->reply_to = message->to = NULL;
  85.   message->id = message->subject = message->splitsize = NULL;
  86.   message->service = message->date = NULL;
  87.   message->content.id = message->content.format = NULL;
  88.   message->content.encoding = message->content.type = NULL;
  89.   message->content.numparms = 0;
  90. }
  91.  
  92. ParseHeader(message, input)
  93.      struct MessageInfo *message;
  94.      FILE *input;
  95. {
  96.   char *s,*c;
  97.   char *Trim(), *malloc(), *fgets(), *strtok();
  98.   char buf[MAXLINELEN],f;
  99.  
  100.   while (fgets(buf, MAXLINELEN, input) != NULL && *Trim(buf))
  101.   {
  102.     while(((f=ungetc(fgetc(input),input))=='\t')||(f==' '))
  103.       {
  104.         char foo[MAXLINELEN];
  105.         /*Get the next line and append it to buf*/
  106.  
  107.         fgets(foo,MAXLINELEN-strlen(buf),input);
  108.     for(c=foo;isspace(*c);c++);
  109.         if(*Trim(c))
  110.       strcpy(buf+strlen(buf),c);
  111.         else
  112.       ungetc('\n',input);
  113.       }     
  114.     if (!strncasecmp(buf, "from:", 5)) {
  115.       StoreAddress(buf+5, &(message->from));
  116.     }
  117.     else if (!strncasecmp(buf, "to:", 3)) {
  118.       StoreAddress(buf+3, &(message->to));
  119.     }
  120.     else if (!strncasecmp(buf, "apparently-to:", 14)) {
  121.       StoreAddress(buf+14, &(message->to));
  122.     }
  123.     else if (!strncasecmp(buf, "x-splitsize:", 12)) {
  124.       StoreAddress(buf+12, &(message->splitsize));
  125.     }
  126.     else if (!strncasecmp(buf, "reply-to:", 9)) {
  127.       StoreAddress(buf+9, &(message->reply_to));
  128.     }
  129.     else if (!strncasecmp(buf, "message-id:", 11)) {
  130.       StoreString(buf+11, &(message->id));
  131.     }
  132.     else if (!strncasecmp(buf, "date:", 5)) {
  133.       StoreString(buf+5, &(message->date));
  134.     }
  135.     else if (!strncasecmp(buf, "content-id:", 11)) {
  136.       StoreString(buf+11, &(message->content.id));
  137.     }
  138.     else if (!strncasecmp(buf, "subject:", 8)) {
  139.       StoreString(buf+8, &(message->subject));
  140.     }
  141.     else if (!strncasecmp(buf, "content-description:", 20)) {
  142.       StoreString(buf+20, &(message->subject));
  143.     }
  144.     else if (!strncasecmp(buf, "x-service:", 10)) {
  145.       StoreString(buf+10, &(message->service));
  146.     }
  147.     else if (!strncasecmp(buf, "content-type:", 13)) {
  148.       StoreString(strtok(buf+13, " /\n\t"), &(message->content.type));
  149.       StoreString(strtok(NULL, " ;\n\t"), &(message->content.format));
  150.       while (s = strtok(NULL, " ,=\n\t")) {
  151.     StoreString(s, &(message->content.parms[message->content.numparms].name));
  152.     s += strlen(s) + 1;
  153.     while (*s == ' ' || *s == '\t' || *s == '=') s++;
  154.     StoreString(strtok(NULL, (*s == '"' ? "\"\n" : ";,\n\t")),
  155.             &(message->content.parms[message->content.numparms++].value));
  156.       }
  157.     }
  158.     else if (!strncasecmp(buf, "content-transfer-encoding:", 26)) {
  159.       StoreString(buf+26, &(message->content.encoding));
  160.     }
  161.   }
  162.   if (message->content.type == NULL) {
  163.     StoreString("text", &(message->content.type));
  164.     StoreString("plain", &(message->content.format));
  165.   }
  166. }
  167.  
  168. StoreAddress(rval, lval)
  169.      char *rval, **lval;
  170. {
  171.   char *s, *t, *strtok(), *strchr();
  172.   
  173.   if (s = strchr(rval, '<'))
  174.     if (s = strtok(s+1, " \t\n>")) StoreString(s, lval);
  175.     else ErrorMsg("cannot parse posterior address");
  176.   else
  177.     if (s = strtok(rval, " \t\n,")) StoreString(s, lval);
  178.     else ErrorMsg("cannot parse anterior address");
  179.   return;
  180. }
  181.  
  182. StoreString(rval, lval)
  183.      char *rval, **lval;
  184. {
  185.   if (rval == NULL) {
  186.     *lval = NULL;
  187.     return;
  188.   }
  189.   if (*lval != NULL) free(*lval);
  190.   while (isspace(*rval)) rval++;
  191.   *lval = malloc(strlen(rval) + 1);
  192.   if (*lval == NULL) ErrorExit("malloc failed");
  193.   strcpy(*lval, rval);
  194. }
  195.  
  196. ParseBody(message, input, boundaries, numboundlval)
  197.      struct MessageInfo *message;
  198.      FILE *input;
  199.      char **boundaries;
  200.      int *numboundlval;
  201. {
  202.   if (!strcasecmp(message->content.type, "multipart"))
  203.     ParseMultipart(message, input);
  204.   else if (!strcasecmp(message->content.type, "message") &&
  205.        !strcasecmp(message->content.format, "external-body"))
  206.     ParseExternal(message, input);
  207.   else if (!strcasecmp(message->content.type, "message") &&
  208.        !strcasecmp(message->content.format, "partial"))
  209.     ParsePartial(message, input);
  210.   else {
  211.     if (message->content.id) strcpy(message->body.fname, message->content.id);
  212.     else MakeFileName(message->body.fname);
  213.     StoreStream(message->content.encoding, input, message->body.fname,
  214.         boundaries, numboundlval);
  215.   }
  216. }
  217.  
  218. ParseMultipart(message, input)
  219.      struct MessageInfo *message;
  220.      FILE *input;
  221. {
  222.   int moreparts;
  223.   int bindex;
  224.   char buf[1024];
  225.  
  226.   for(bindex=0; bindex < message->content.numparms; bindex++)
  227.     if (!strcasecmp(message->content.parms[bindex].name, "boundary")) break;
  228.   if (bindex >= message->content.numparms)
  229.     ErrorExit("multipart missing boundary parameter");
  230.    
  231.   /*Tack on the mandatory '--' to the boundary*/
  232.   sprintf(buf,"--%s",message->content.parms[bindex].value);
  233.   strcpy(message->content.parms[bindex].value,buf);
  234.   /* burn off prologue */
  235.   moreparts = 1;
  236.   from7bit(input, NULL, &(message->content.parms[bindex].value), &moreparts);
  237.   
  238.   /* identify, parse message, and store each part (there may be none) */
  239.   message->body.multipart.parts =
  240.     (struct MessageInfo **) calloc(MAXPARTS, sizeof(struct MessageInfo *));
  241.   message->body.multipart.numparts = 0;
  242.   while (moreparts && message->body.multipart.numparts < MAXPARTS) {
  243.     struct MessageInfo *m;
  244.     
  245.     message->body.multipart.parts[message->body.multipart.numparts] =
  246.       m = (struct MessageInfo *) malloc(sizeof(struct MessageInfo));
  247.     if (m == NULL) ErrorExit("malloc of messageinfo failed");
  248.     InitHeader(m);
  249.     ParseHeader(m, input);
  250.     ParseBody(m, input, &(message->content.parms[bindex].value), &moreparts);
  251.     message->body.multipart.numparts++;
  252.   }
  253.   if (moreparts) ErrorMsg("too many parts");
  254.   
  255.   /* ignore epilogue */
  256. }
  257.  
  258. ParseExternal(message, input)
  259.      struct MessageInfo *message;
  260.      FILE *input;
  261. {
  262.   char buf[256], newfile[MAXFILENAMELEN];
  263.   FILE *fopen(), *f;
  264.   int ai, ni, di, si, mi, i;
  265.   
  266.   ai = ni = di = si = mi = -1;
  267.   for(i=0; i < message->content.numparms; i++)
  268.     if (!strcasecmp(message->content.parms[i].name, "access-type")) ai=i;
  269.     else if (!strcasecmp(message->content.parms[i].name, "name")) ni=i;
  270.     else if (!strcasecmp(message->content.parms[i].name, "site")) si=i;
  271.     else if (!strcasecmp(message->content.parms[i].name, "directory")) di=i;
  272.     else if (!strcasecmp(message->content.parms[i].name, "mode")) mi=i;
  273.   if (ai < 0 || ni < 0) ErrorExit("external-body must have an access-type and name");
  274.   
  275.   MakeFileName(newfile);
  276.   sprintf(buf, "grabexternal %s %s %s %s %s %s", newfile,
  277.       message->content.parms[ai].value, message->content.parms[ni].value,
  278.       si>=0 ? message->content.parms[si].value : "",
  279.       si>=0 && di>=0 ? message->content.parms[di].value : "",
  280.       si>=0 && di>=0 && mi>=0 ? message->content.parms[mi].value : "");
  281.   system(buf);
  282.   message->content.id = message->content.format = NULL;
  283.   message->content.encoding = message->content.type = NULL;
  284.   message->content.numparms = 0;
  285.   ParseHeader(message, input);
  286.   f = fopen(newfile, "r");
  287.   if (f == NULL) ErrorMsg("can't get external file");
  288.   else {
  289.     ParseBody(message, f, NULL, NULL);
  290.     fclose(f);
  291.   }
  292.   unlink(newfile);
  293. }    
  294.  
  295. ParsePartial(message, input)
  296.      struct MessageInfo *message;
  297.   FILE *input;
  298. {
  299.   int numsort(), numselect(), atoi();
  300.   int i, idi, numi, totali, numparts;
  301.   struct direct **files;
  302.   char storagedir[MAXFILENAMELEN], storagefile[MAXFILENAMELEN], cmd[256];
  303.   FILE *fopen(), *ct;
  304.   
  305.   /* extract certain parameters from the content-type field */
  306.   idi = numi = totali = -1;
  307.   for(i=0; i < message->content.numparms; i++)
  308.     if (!strcasecmp(message->content.parms[i].name, "id")) idi=i;
  309.     else if (!strcasecmp(message->content.parms[i].name, "number")) numi=i;
  310.     else if (!strcasecmp(message->content.parms[i].name, "total")) totali=i;
  311.   
  312.   if (idi >= 0 && numi >= 0) {
  313.     sprintf(storagedir, "%s/%s", TEMPDIR, message->content.parms[idi].value);
  314.     /* create the directory if it doesn't exist.  For now, this approach
  315.        doesn't deal well with mkdir errors */
  316.     mkdir(storagedir, 0700);
  317.     
  318.     /* this strategy for storing pieces doesn't follow the MIME document's
  319.        prescription for creating the resulting header, but it does follow the
  320.        de facto method suggested by metamail's splitmail(1) command */
  321.     sprintf(storagefile, "%s/%s", storagedir, message->content.parms[numi].value);
  322.     StoreStream(NULL, input, storagefile, NULL, NULL);
  323.     sprintf(storagefile, "%s/CT", storagedir);
  324.     if (totali >= 0) {
  325.       ct = fopen(storagefile, "w");
  326.       fputs(message->content.parms[totali].value, ct);
  327.       fclose(ct);
  328.     }
  329.     
  330.     /* okay, now see if all parts have arrived */
  331.     ct = fopen(storagefile, "r");
  332.     if (ct != NULL) { /* we must know the total */
  333.       fscanf(ct, "%d", &numparts);
  334.       fclose(ct);
  335.       if (numparts == scandir(storagedir, &files, numselect, numsort)) {
  336.     sprintf(cmd, "cd %s; cat", storagedir);
  337.     while (numparts--) { /* This could be more efficient by using an endpointer */
  338.       strcat(cmd, " ");
  339.       strcat(cmd, (*files++)->d_name);
  340.     }
  341.     
  342.     /* now, recursively process the message -- the current message will end
  343.        up being ignored since it is a message/partial */
  344.     ct = popen(cmd, "r");
  345.     HandleMessage(ct);
  346.     pclose(ct);
  347.     sprintf(cmd, "rm -r %s", storagedir);
  348.     system(cmd);
  349.       }
  350.     }
  351.   }
  352.   else ErrorExit("Partial message must have id and number parameters.");
  353. }
  354.  
  355. /* the next three functions are support for the scandir call made in ParsePartial */
  356.  
  357. int numselect(d)
  358.      struct direct *d;
  359. {
  360.   char *s;
  361.   
  362.   for (s=d->d_name; *s; s++) if (!isdigit(*s)) return 0;
  363.   return 1;
  364. }
  365.  
  366. int numsort(d1, d2)
  367.      struct direct **d1, **d2;
  368. {
  369.   return(icomp(atoi((*d1)->d_name), atoi((*d2)->d_name)));
  370. }
  371.  
  372. int icomp(i1, i2)
  373.      int i1, i2;
  374. {
  375.   return(i1<i2 ? -1 : (i1>i2));
  376. }
  377.  
  378. StoreStream(encoding, instream, fname, boundaries, numboundlval)
  379.      char *encoding;
  380.      FILE *instream;
  381.      char *fname;
  382.      char **boundaries;
  383.      int *numboundlval;
  384. {
  385.   FILE *outstream;
  386.   FILE *fopen();
  387.   
  388.   outstream = fopen(fname, "w");
  389.   if (encoding && !strcasecmp(encoding, "base64"))
  390.     from64(instream, outstream, boundaries, numboundlval);
  391.   else if (encoding && !strcasecmp(encoding, "quoted-printable"))
  392.     fromqp(instream, outstream, boundaries, numboundlval);
  393.   else
  394.     from7bit(instream, outstream, boundaries, numboundlval);
  395.   fclose(outstream);
  396. }
  397.  
  398. MakeFileName(buf)
  399.      char *buf;
  400. {
  401.   static int ctr = 0;
  402.   sprintf(buf, "part%d", ctr++);
  403. }
  404.  
  405. FreeGarbage(message)
  406.      struct MessageInfo message;
  407. {
  408. }
  409.  
  410. from7bit(infile, outfile, boundaries, boundaryct) 
  411.      FILE *infile, *outfile;
  412.      char **boundaries;
  413.      int *boundaryct;
  414. {
  415.   char buf[1000], *b;
  416.   int sawnewline = 2;
  417.   
  418.   while (fgets(buf, sizeof(buf), infile) != NULL) {
  419.     if (sawnewline && boundaries && buf[0] == '-' && buf[1] == '-' &&
  420.     PendingBoundary(buf, boundaries, boundaryct)) return;
  421.     else {
  422.       if (outfile) {
  423.     if (sawnewline == 1) putc('\n', outfile);
  424.     sawnewline = 0;
  425.     for(b=buf; b; b++)
  426.       if (*b == '\n') { sawnewline = 1; break; }
  427.       else putc(*b, outfile); 
  428.       }
  429.       else sawnewline = (buf[strlen(buf)-1] == '\n');
  430.     }
  431.   }
  432. }
  433.